package com.fm.demo.widget

import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.graphics.*
import android.graphics.drawable.Drawable
import android.view.animation.LinearInterpolator
import java.util.*
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin


class FishDrawable : Drawable() {
    /**
     * 头部半径
     */
    private val _headRadius = 30f

    /**
     * 第一节身体长度
     */
    private val _bodyLength = _headRadius * 3.2f

    /**
     * 身体透明度
     */
    private val _bodyAlpha = 220

    /**
     * 鱼鳍透明度
     */
    private val _finsAlpha = 100

    /**
     * 其他部分透明度
     */
    private val _otherAlpha = 160

    /**
     * 左鱼鳍
     */
    private val _finsLeft = 1

    /**
     * 右鱼鳍
     */
    private val _finsRight = -1

    /**
     * 鱼鳍的两个控制点的长度，即鱼鳍的宽度
     */
    private val _finsLength = _headRadius * 1.3f

    /**
     * 鱼的长度
     */
    private val _fishLength = _headRadius * 6.79f

    /**
     * 动画控制引擎变量值
     */
    private var _currentValue = 0f
    private var _mainAngle = 90f
    private var _finsAnimator: ObjectAnimator = ObjectAnimator()

    /**
     * 全局频率，三角函数里面的
     */
    private var _waveFrequence = 1f

    /**
     * 鱼点头
     */
    private var _headPoint = PointF()

    /**
     * 重心
     */
    private val _middlePoint: PointF = PointF()
    private var _finsAngle = 0f

    /**
     * 身体的画笔
     */
    private val _bodyPaint: Paint = Paint()
    private val _path: Path = Path()

    /**
     * 主画笔
     */
    private val _paint: Paint = Paint()


    init {
        //主画笔
        _paint.let {
            it.isAntiAlias = true
            it.style = Paint.Style.FILL
            it.isDither = true
            it.color = Color.argb(_otherAlpha, 244, 92, 71)
        }
        _bodyPaint.let {
            it.isAntiAlias = true
            it.style = Paint.Style.FILL
            it.isDither = true
            it.color = Color.argb(_otherAlpha, 244, 92, 71)
        }
        //将重心坐标设置为重心
        _middlePoint.let {
            it.x = 4.18f * _headRadius
            it.y = 4.18f * _headRadius
        }
        //鱼鳍灵动的动画等
        _finsAnimator.let {
            it.target = this
            it.setFloatValues(0f, 1f, 0f)
            it.setPropertyName("_finsAngle")
            it.repeatMode = ValueAnimator.REVERSE
            it.repeatCount = Random().nextInt(3)
        }
        //动画引擎
        val valueAnimator = ValueAnimator.ofFloat(0f, 540 * 100f)
        valueAnimator.let { animator ->
            animator.duration = 180 * 3000
            animator.interpolator = LinearInterpolator()
            animator.repeatCount = ValueAnimator.INFINITE
            animator.repeatMode = ValueAnimator.REVERSE
            animator.addUpdateListener {
                //引擎
                _currentValue = it.animatedValue as Float
                invalidateSelf()
            }
            animator.addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator?) {
                    _finsAnimator.start()
                }

                override fun onAnimationEnd(animation: Animator?) {
                }

                override fun onAnimationCancel(animation: Animator?) {
                }

                override fun onAnimationRepeat(animation: Animator?) {
                }
            })
        }
        valueAnimator.start()

    }


    override fun draw(canvas: Canvas) {
        //设置一个半透明图层，避免与白色背景形成干扰，并且尺寸必须鱼view的大小一致否则鱼显示不完全
        canvas.saveLayerAlpha(0f, 0f, bounds.width().toFloat(), bounds.height().toFloat(), 240)
        makeBody(canvas)
        canvas.restore()
        _path.reset()
        //恢复颜色
        _paint.color = Color.argb(_otherAlpha, 244, 92, 71)
    }

    /**
     * 画鱼的身子
     * <p>
     *     主方向是头到尾的方向跟x轴方向的夹角
     *     前进方向跟主方向相差180度
     * @param canvas
     */
    private fun makeBody(canvas: Canvas) {
        //现在的角度 = 原始角度 + sin(域值) * 可摆动的角度       sin是用来控制周期运动的
        val angle = _mainAngle + sin(Math.toRadians(_currentValue * 1.2 * _waveFrequence)) * 2

        //假设现在BODY_LENGH / 2线段在x轴上，旋转90度得到头部终点坐标
        _headPoint = calculatPoint(_middlePoint, _bodyLength / 2, _mainAngle)
        //画头
        canvas.drawCircle(_headPoint.x, _headPoint.y, _headRadius, _paint)

        //右鱼鳍  右鱼鳍轴的起点  起点相对鱼头方向顺时针转了110度
        val pointFFinsRight = calculatPoint(_headPoint, _headRadius * 0.9f, (angle - 110).toFloat())
        makeFins(canvas, pointFFinsRight, _finsRight, angle.toFloat())
        //左鱼鳍
        val pointFFinsLeft = calculatPoint(_headPoint, _headRadius * 0.9f, (angle + 110).toFloat())
        makeFins(canvas, pointFFinsLeft, _finsLeft, angle.toFloat())

        //躯干底部的圆位置   相对鱼头方向旋转180度
        val endPointF = calculatPoint(_headPoint, _bodyLength, (angle - 180).toFloat())

        //大躯干的下面的躯干2,
        val mainPointF = PointF(endPointF.x, endPointF.y)
        makeSegments(canvas, mainPointF, _headRadius * 0.7f, 0.6f, angle)

        //鱼的最大身子
        val pointF1: PointF = calculatPoint(_headPoint, _headRadius, (angle - 80).toFloat()) //80度得到的点偏上一点，看起来会更好，右上
        val pointF4: PointF = calculatPoint(_headPoint, _headRadius, (angle + 80).toFloat()) //左上
        val pointF2: PointF = calculatPoint(endPointF, _headRadius * 0.7f, (angle - 90).toFloat()) //右下
        val pointF3: PointF = calculatPoint(endPointF, _headRadius * 0.7f, (angle + 90).toFloat()) //左下

        //贝塞尔曲线控制点
        val controlRightPointF: PointF = calculatPoint(_headPoint, _bodyLength * 0.56f, (angle - 130).toFloat()) //右控制点
        val controlLeftPointF: PointF = calculatPoint(_headPoint, _bodyLength * 0.56f, (angle + 130).toFloat())


        _path.reset()
        _path.moveTo(pointF1.x, pointF1.y)
        _path.quadTo(controlRightPointF.x, controlRightPointF.y, pointF2.x, pointF2.y)
        _path.lineTo(pointF3.x, pointF3.y)

        _path.quadTo(controlLeftPointF.x, controlLeftPointF.y, pointF4.x, pointF4.y)
        _path.lineTo(pointF1.x, pointF1.y)
        //画出鱼最大身子
        _paint.color = Color.argb(_bodyAlpha, 244, 92, 71)
        canvas.drawPath(_path, _paint)
    }

    /**
     * 画第二节节肢
     *
     * @param canvas
     * @param mainPointF       该段的顶部圆心的坐标
     * @param segmentTopRadius 该段的顶部半径
     * @param ratio            梯形上下比例
     * @param fatherAngle      鱼头方向（父控件），默认为90
     */
    @Suppress("SameParameterValue")
    private fun makeSegments(canvas: Canvas, mainPointF: PointF, segmentTopRadius: Float, ratio: Float, fatherAngle: Double) {
        //中心轴线和X轴顺时针方向夹角，15度左右摇摆
        val angle = (fatherAngle + cos(Math.toRadians(_currentValue * 1.5 * _waveFrequence)) * 15).toFloat()

        //身长
        val segmentLength = segmentTopRadius * (ratio + 1)
        //底部圆心坐标
        val bottomPointF = calculatPoint(mainPointF, segmentLength, angle - 180)
        val pointF1: PointF
        val pointF2: PointF
        val pointF3: PointF
        val pointF4: PointF
        //梯形4个角的坐标
        pointF1 = calculatPoint(mainPointF, segmentTopRadius, angle - 90) //左上
        pointF4 = calculatPoint(mainPointF, segmentTopRadius, angle + 90) //右上
        pointF2 = calculatPoint(bottomPointF, segmentTopRadius * ratio, angle - 90) //左下
        pointF3 = calculatPoint(bottomPointF, segmentTopRadius * ratio, angle + 90) //右下

        //画上下俩个圆
        canvas.drawCircle(mainPointF.x, mainPointF.y, segmentTopRadius, _paint)
        canvas.drawCircle(bottomPointF.x, bottomPointF.y, segmentTopRadius * ratio, _paint)
        _path.reset()
        _path.moveTo(pointF1.x, pointF1.y)
        _path.lineTo(pointF4.x, pointF4.y)
        _path.lineTo(pointF3.x, pointF3.y)
        _path.lineTo(pointF2.x, pointF2.y)
        canvas.drawPath(_path, _paint)

        //躯干2的下面的躯干，和躯干2差不多
        val topPointF2 = PointF(bottomPointF.x, bottomPointF.y)
        makeSegmentLong(canvas, topPointF2, segmentTopRadius * 0.6f, 0.4f, angle)
    }

    /**
     * 画第三节节肢
     *
     * @param canvas
     * @param topPointF2       该段的顶部圆心的坐标
     * @param segmentTopRadius 该段的顶部半径
     * @param ratio            梯形上下比例
     * @param fatherAngle      上面控件方向，默认为90
     */
    @Suppress("SameParameterValue")
    private fun makeSegmentLong(canvas: Canvas, topPointF2: PointF, segmentTopRadius: Float, ratio: Float, fatherAngle: Float) {
        //中心轴线和X轴顺时针方向夹角,这里为35度比上面部分控件越来越大，扭动时候可以体现出鱼灵动的摇尾巴的动作
        val angle = (fatherAngle + sin(Math.toRadians(_currentValue * 1.5 * _waveFrequence)) * 35).toFloat()
        //身长
        val segmentLength = segmentTopRadius * (ratio + 2.7f)
        //同上面
        val bottomPointF = calculatPoint(topPointF2, segmentLength, angle - 180)
        val pointF1: PointF
        val pointF2: PointF
        val pointF3: PointF
        val pointF4: PointF
        //梯形4个角的坐标
        pointF1 = calculatPoint(topPointF2, segmentTopRadius, angle - 90) //左上
        pointF4 = calculatPoint(topPointF2, segmentTopRadius, angle + 90) //右上
        pointF2 = calculatPoint(bottomPointF, segmentTopRadius * ratio, angle - 90) //左下
        pointF3 = calculatPoint(bottomPointF, segmentTopRadius * ratio, angle + 90) //右下


//        makeTail(canvas, topPointF2, segmentLength, segmentTopRadius, angle);
        //为了扭得更骚，加了50宽度
        makeTail(canvas, topPointF2, segmentLength, segmentTopRadius + 50, angle)
        //画上下俩个圆
        canvas.drawCircle(bottomPointF.x, bottomPointF.y, segmentTopRadius * ratio, _paint)
        _path.reset()
        _path.moveTo(pointF1.x, pointF1.y)
        _path.lineTo(pointF4.x, pointF4.y)
        _path.lineTo(pointF3.x, pointF3.y)
        _path.lineTo(pointF2.x, pointF2.y)
        canvas.drawPath(_path, _paint)
    }

    /**
     * 画尾巴
     *
     * @param topPointF2 上个控件的底部圆心，
     * @param length     上个控件的底部圆半径
     * @param maxWidth   最大的宽度，用来确定三角形的左右俩宽度不越界
     * @param angle      上级控件的当前角度
     */
    private fun makeTail(canvas: Canvas, topPointF2: PointF, length: Float, maxWidth: Float, angle: Float) {
        //三角形的最大2分之宽度，随三角函数变化
        val newWidth = (abs(sin(Math.toRadians(_currentValue * 1.7 * _waveFrequence))) * maxWidth + _headRadius * 1 / 5).toFloat()

        //大小三角形的底边中点，1为大
        val bottomPointF1 = calculatPoint(topPointF2, length, angle - 180)
        val bottomPointF2 = calculatPoint(topPointF2, length - 10, angle - 180) //小的

        //2个三角形的左右顶点，共4个
        val pointF1: PointF
        val pointF2: PointF
        val pointF3: PointF
        val pointF4: PointF
        pointF1 = calculatPoint(bottomPointF1, newWidth, angle - 90) //右定点 大三角形
        pointF4 = calculatPoint(bottomPointF1, newWidth, angle + 90) //左
        pointF2 = calculatPoint(bottomPointF2, newWidth - 20, angle - 90) //右定点， 小三角形   这里用10和20都可以
        pointF3 = calculatPoint(bottomPointF2, newWidth - 20, angle + 90) //左

        //小三角形
        _path.reset()
        _path.moveTo(topPointF2.x, topPointF2.y)
        _path.lineTo(pointF2.x, pointF2.y)
        _path.lineTo(pointF3.x, pointF3.y)
        _path.lineTo(topPointF2.x, topPointF2.y)
        canvas.drawPath(_path, _paint)

        //大三角形
        _path.reset()
        _path.moveTo(topPointF2.x, topPointF2.y)
        _path.lineTo(pointF1.x, pointF1.y)
        _path.lineTo(pointF4.x, pointF4.y)
        _path.lineTo(topPointF2.x, topPointF2.y)
        canvas.drawPath(_path, _paint)
    }


    override fun setAlpha(alpha: Int) {
        _paint.alpha = alpha
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        _paint.colorFilter = colorFilter
    }

    override fun getOpacity(): Int {
        //半透明，只会绘制的地方才盖住下方
        return PixelFormat.TRANSLUCENT
    }

    override fun getIntrinsicHeight(): Int {
        return (8.38f * _headRadius).toInt()
    }

    override fun getIntrinsicWidth(): Int {
        return (8.38f * _headRadius).toInt()
    }

    /**
     * @param canvas
     * @param pointFFinsRight 鱼鳍主轴的起点
     * @param type
     * @param angle           鱼头的方向
     */
    private fun makeFins(canvas: Canvas, pointFFinsRight: PointF, type: Int, angle: Float) {
        val controlAngle = 110f //鱼鳍控制点相对于鱼主轴方向的角度，模型示意图中是110
        _path.reset()
        _path.moveTo(pointFFinsRight.x, pointFFinsRight.y)
        //计算鱼鳍主轴的终点，按照模型图（博客里）,如果是右鱼鳍，那么按照模型图，鱼的角度减去鱼鳍扭的角度还有减去180度就是鱼主轴的另外一个方向
        val endPointF = calculatPoint(pointFFinsRight, _finsLength, if (type == _finsRight) angle - _finsAngle - 180 else angle + _finsAngle + 180)
        //贝塞尔曲线辅助控制点
        val controlPointF = calculatPoint(
            pointFFinsRight, _finsLength * 1.8f, if (type == _finsRight) {
                angle - _finsAngle - controlAngle
            } else {
                angle + _finsAngle + controlAngle
            }
        )
        //画贝塞尔曲线
        _path.quadTo(controlPointF.x, controlPointF.y, endPointF.x, endPointF.y)
        _path.lineTo(pointFFinsRight.x, pointFFinsRight.y)
        _paint.color = Color.argb(_finsAlpha, 244, 92, 71)
        canvas.drawPath(_path, _paint)
        _paint.color = Color.argb(_otherAlpha, 244, 92, 71)
    }

    /**
     * 输入起点、长度、旋转角度计算终点
     * <p>
     * 知道一个线段，一个定点，线段旋转角度求终点坐标
     * 根据极坐标系原理 x = pcog(a), y = psin(a)
     *
     * @param startPoint 起点
     * @param length     长度
     * @param angle      旋转角度
     * @return 计算结果点
     */
    private fun calculatPoint(startPoint: PointF, length: Float, angle: Float): PointF {
        val deltaX = cos(Math.toRadians(angle.toDouble())).toFloat() * length
        //符合Android坐标的y轴朝下的标准，和y轴有关的统一减180度
        val deltaY = sin(Math.toRadians((angle - 180).toDouble())).toFloat() * length
        return PointF(startPoint.x + deltaX, startPoint.y + deltaY)
    }

    fun setMainAngle(mainAngle: Float) {
        _mainAngle = mainAngle
    }

    fun getHeadRadius(): Float {
        return _headRadius
    }

    fun setWaveFrequence(waveFrequence: Float) {
        _waveFrequence = waveFrequence
    }

    fun getFinsAnimator(): ObjectAnimator {
        return _finsAnimator
    }

    fun getMiddlePoint(): PointF {
        return _middlePoint
    }

    fun getHeadPoint(): PointF {
        return _headPoint
    }
}